/**
*  \class Matrix
*  \brief Implements a N-dimensional matrix and its common operations.
*
*  <b>Related documentation:</b>
*  - \ref dimlimits "Matrix Dimensional Limits"
*
*  \author Marco Gregnanin
*  \author Riccardo Marson
*  \version 1.13.0a
*  \date    8 February 2011
*/


#ifndef MATRIX_H
#define MATRIX_H


#include "mathlib.h"
#include <new>
#include <iostream>

#define MATRIX_NAME         "Matrix"
#define DIMENSION_OVERFLOW  QObject::tr( "Too many dimensions." )
#define INNER_DIM_MISMATCH  QObject::tr( "Inner dimensions of matrices do not agree." )
#define LEN_INDEX_OVERFLOW  QObject::tr( "Index exceeds matrix length." )
#define MATRIX_NOT_SQUARE   QObject::tr( "Matrix is not square." )
#define NO_DATA_AVAILABLE   QObject::tr( "Matrix with 0 length has no data." )
#define SIZE_MISMATCH       QObject::tr( "Matrices size must agree." )
#define UNKNOWN_OPERATION   QObject::tr( "The required operation in unknown." )

enum { EQUAL_TO, NOT_EQUAL_TO, GREATER_THAN, GREATER_THAN_EQUAL_TO, LOWER_THAN, LOWER_THAN_EQUAL_TO };


using namespace std;


namespace MathLib
{
    template <class DataType, typename LengthType, unsigned char dimensions>
    class MATHLIBSHARED_EXPORT Matrix
    {
        // Private members
        bool        square:1;           //!< Square matrix flag
        DataType    *data;              //!< Content of the matrix
        DataType    *row;               //!< Array containing the temporary data value of a row
        LengthType  *size;              //!< Array containing the length of each dimension
        LengthType  i;                  //!< Preallocated index
        LengthType  j;                  //!< Preallocated index
        LengthType  k;                  //!< Preallocated index
        LengthType  length;             //!< Total number of matrix elements
        const QString className;        //!< Internal name of the class

        // Private methods
        inline Matrix<bool,LengthType,dimensions> comparison( const Matrix &right_hand, unsigned char operation );
                                                                            //!< Generic matrix/matrix comparison
        inline Matrix<bool,LengthType,dimensions> comparison( const DataType &right_hand, unsigned char operation );
                                                                            //!< Generic matrix/scalar comparison

#ifdef DEBUG
        inline void checkSizeMismatch( const Matrix &right_hand );          //!< Check for SIZE_MISMATCH exception (only in DEBUG mode).
#endif

    public:

        // Public members
        unsigned int rowscount;
        unsigned int columnscount;

        // Public methods

        // Constructors and destructor
        Matrix();                                                           //!< Empty matrix constructor
        Matrix(const LengthType (&input_size)[dimensions]);                 //!< Matrix constructor
        Matrix(const Matrix &m);                                            //!< Matrix copy constructor
        ~Matrix();                                                          //!< Matrix destructor

        // Accessors
        inline unsigned char getDimensions();                               //!< Get matrix dimensions
        inline LengthType getLength();                                      //!< Get matrix length
        inline LengthType getSize( unsigned char dimension );               //!< Get the size of a matrix dimension

        // Operator overloads
        inline DataType& operator() (const LengthType& i);                      //!< () operator overload.
        inline DataType& operator() (const LengthType& i, const LengthType& j); //!< () operator overload.
        inline Matrix& operator= ( const Matrix &right_hand );                  //!< = operator overload.
        inline Matrix& operator= ( const DataType &right_hand );                //!< = operator overload.
        inline Matrix& operator+= ( const Matrix &right_hand );                 //!< += operator overload.
        inline Matrix& operator+= ( const DataType &right_hand );               //!< += operator overload.
        inline Matrix operator+ ( const Matrix &right_hand );                   //!< + operator overload.
        inline Matrix operator+ ( const DataType &right_hand );                 //!< + operator overload.
        inline Matrix& operator-= ( const Matrix &right_hand );                 //!< -= operator overload.
        inline Matrix& operator-= ( const DataType &right_hand );               //!< -= operator overload.
        inline Matrix operator- ( const Matrix &right_hand );                   //!< '-' operator overload.
        inline Matrix operator- ( const DataType &right_hand );                 //!< '-' operator overload.
        inline Matrix<DataType,LengthType,2>& operator*= ( const Matrix<DataType,LengthType,2> &right_hand );
                                                                                //!< *= operator overload.
        inline Matrix& operator*= ( const DataType &right_hand );               //!< *= operator overload.
        inline Matrix<DataType,LengthType,2> operator* ( const Matrix<DataType,LengthType,2> &right_hand );
                                                                                //!< * operator overload.
        inline Matrix<DataType,LengthType,1> operator* ( const Matrix<DataType,LengthType,1> &right_hand );
                                                                                //!< * operator overload.
        inline Matrix operator* ( const DataType &right_hand );                 //!< * operator overload.
        inline Matrix<bool,LengthType,dimensions> operator== ( const Matrix &right_hand );
                                                                                //!< == operator overload.
        inline Matrix<bool,LengthType,dimensions> operator== ( const DataType &right_hand );
                                                                                //!< == operator overload.
        inline Matrix<bool,LengthType,dimensions> operator!= ( const Matrix &right_hand );
                                                                                //!< != operator overload.
        inline Matrix<bool,LengthType,dimensions> operator!= ( const DataType &right_hand );
                                                                                //!< != operator overload.
        inline Matrix<bool,LengthType,dimensions> operator> ( const Matrix &right_hand );
                                                                                //!< > operator overload.
        inline Matrix<bool,LengthType,dimensions> operator> ( const DataType &right_hand );
                                                                                //!< > operator overload.
        inline Matrix<bool,LengthType,dimensions> operator>= ( const Matrix &right_hand );
                                                                                //!< >= operator overload.
        inline Matrix<bool,LengthType,dimensions> operator>= ( const DataType &right_hand );
                                                                                //!< >= operator overload.
        inline Matrix<bool,LengthType,dimensions> operator< ( const Matrix &right_hand );
                                                                                //!< < operator overload.
        inline Matrix<bool,LengthType,dimensions> operator< ( const DataType &right_hand );
                                                                                //!< < operator overload.
        inline Matrix<bool,LengthType,dimensions> operator<= ( const Matrix &right_hand );
                                                                                //!< <= operator overload.
        inline Matrix<bool,LengthType,dimensions> operator<= ( const DataType &right_hand );
                                                                                //!< <= operator overload.
        inline Matrix& copy( const Matrix &right_hand );                        //!< Copy operator.

        // Property accessors
        inline bool isEqualTo( const Matrix &right_hand );                  //!< Compares matrices
        inline bool isSquare();                                             //!< Tells if matrix is square
        inline bool isSymmetric();                                          //!< Tells if matrix is symmetric

        // Linear algebra
        inline DataType trace();                                            //!< Trace of the matrix
        inline Matrix<DataType,LengthType,1> diag();                        //!< Diagonal of the matrix
        inline Matrix transpose();                                          //!< Get the transpose of the matrix
        Matrix cholesky();                                                  //!< Cholesky decomposition
        bool inv();                                                         //!< Inverse of the matrix

#ifdef DEBUG
        // Debug only
        void print( char format[] );                                         //!< Print the matrix
#endif
    };
}


#endif // MATRIX_H


/*! \page dimlimits Matrix Dimensional Limits
The maximum number of dimensions allowed is 63, starting from 0 (empty matrix).<br>
In fact, if the size of each dimension is 2, the total number of elements would be 2<sup>63</sup>, and, since the maximum value
storable in an 8 byte <em>unsigned integer</em> is 2<sup>64</sup> \f - \f 1, a 64<sup>th</sup> dimension with size 2 would not be possible,
for indexing reasons.<br><br>
Those limits are referred to the largest handled matrix for this library, but one can further reduce the maximum dimensions allowed
(for performance reasons), instantiating the class, using a smaller unsigned type as second template parameter.<br><br>
In the following table are reported the dimensional limitations for each possible type of <em>LengthType</em>.<br><br>
TBC...
*/
